﻿using System;
using System.Collections.Immutable;
using Sustainalytics.Events.Exceptions;
using Sustainalytics.Events.Service.Errors;

namespace Sustainalytics.Events.Service
{
    public class EventValidationMapper 
    {
        public static EventValidationMapper Instance = new EventValidationMapper();

        private ImmutableDictionary<int, Func<ValidationResult, ValidationResultDto>> _eventValidationMapRules;
        private int _validationFailedCode = -1;
       
        public EventValidationMapper()
        {
            var builder = ImmutableDictionary<int, Func<ValidationResult, ValidationResultDto>>.Empty.ToBuilder();
           
            AddValidationRuleMapping(builder, EventValidationCodes.BodyNullOrEmpty, EventsErrorCodes.Events_EventNotValid_BodyNullOrEmpty);
            AddValidationRuleMapping(builder, EventValidationCodes.LocationNullOrEmpty, EventsErrorCodes.Events_EventNotValid_LocationNullOrEmpty);
            AddValidationRuleMapping(builder, EventValidationCodes.NameNullOrEmpty, EventsErrorCodes.Events_EventNotValid_NameNullOrEmpty);
            AddValidationRuleMapping(builder, EventValidationCodes.NullEvent, EventsErrorCodes.Events_EventNotValid_NullEvent);
            
            AddValidationRuleMapping(builder, EventValidationCodes.NameLenghtInvalid, EventsErrorCodes.Events_EventNotValid_NameLength);
            AddValidationRuleMapping(builder, EventValidationCodes.PresenterLenghtInvalid, EventsErrorCodes.Events_EventNotValid_PresenterLength);
            AddValidationRuleMapping(builder, EventValidationCodes.LocationLenghtInvalid, EventsErrorCodes.Events_EventNotValid_LocationLength);

            this._eventValidationMapRules = builder.ToImmutable();
            this._validationFailedCode = (int)EventsErrorCodes.Events_EventNotValid;
        }

        private void AddValidationRuleMapping(ImmutableDictionary<int, Func<ValidationResult, ValidationResultDto>>.Builder builder, int source, EventsErrorCodes destination)
        {
            builder.Add(source, (r) => this.Map((int) destination, r));
        }

        public ValidationErrorDto Map(EventValidationException validationException)
        {
            if (validationException == null)
                return null;

            var dto = new ValidationErrorDto(_validationFailedCode);
            foreach (var result in validationException.Results)
            {
                if (result == null)
                    continue;

                var resultMapper = GetValidationResultMapper(result);
                var resultDto = resultMapper(result);
                
                if(resultDto != null)
                    dto.Add(resultDto);
            }

            return dto;
        }

      
        public ValidationResultDto Map(int code, ValidationResult result)
        {
            if (result == null)
                return null;

            var validationResultDto = new ValidationResultDto();

            validationResultDto.Code = code;
            validationResultDto.Description = result.Description;
            validationResultDto.IsError = result.IsError;

            return validationResultDto;
        }
      
       
        private Func<ValidationResult, ValidationResultDto> GetValidationResultMapper(ValidationResult result)
        {
            Func<ValidationResult, ValidationResultDto> resultMapper;
            this._eventValidationMapRules.TryGetValue(result.Code, out resultMapper);

            if (resultMapper == null)
                resultMapper = (r) => this.Map(-1, r);

            return resultMapper;
        }
    }


    
}
